local ListView = import(".ListView")
local ListViewCellModelMetatable = import(".ListViewCellModelMetatable")

local ListViewVertical = class("ListViewVertical", ListView)

local SCROLL_SPEED = 20

function ListViewVertical:ctor(scrollview, params)
    -- body
    ListViewVertical.super.ctor(self, scrollview, params)
    local scrollType = self._scrollView:getDirection()
    assert(ccui.ScrollViewDir.vertical == scrollType, 'ListViewVertical only support VERTICAL')
    self._curOffsetY = 0
end

--- public
-- extendOffset 额外的 输入正值
function ListViewVertical:getOffsetByIndex(index, extendOffset)
    local cellModel = self._cellModels[index]
    if not cellModel then
        -- release_print("getOffsetByIndex index out of boundary")
        return 0
    end
    local offsetY = -cellModel.pos.y + self._paddingTop
    offsetY = self._listViewheight + offsetY - self._totalHeight
    if extendOffset then
        offsetY = offsetY - extendOffset
    end
    if offsetY > 0 then
        offsetY = 0
    end
    if offsetY < self._listViewheight - self._totalHeight then
        offsetY = self._listViewheight - self._totalHeight
    end
    return offsetY
end


function ListViewVertical:setListViewOffsetY(offsetY)
    self:_reCreate(offsetY)
    self._innerContainer:setPositionY(offsetY)
end

function ListViewVertical:jumpToIndex(index, extendOffset)
    local offsetY = self:getOffsetByIndex(index, extendOffset)
    self:setListViewOffsetY(offsetY)
end

function ListViewVertical:stopScrollAction(isEnd)
    if self._curScrollAction then
        self._scrollView:stopAction(self._curScrollAction)
        self._curScrollAction = nil
        if self._scrollEndCallBack then
            self._scrollEndCallBack(isEnd)
        end
        self._scrollEndCallBack = nil
    end
end

function ListViewVertical:scrollToIndex(index, endCallback, extendOffset, speed)
    local offsetY = self:getOffsetByIndex(index, extendOffset)
    self:scrollToOffset(offsetY, endCallback, speed)
end


function ListViewVertical:scrollToVisibleArea(index, endCallback, speed)
    if not self:isInVisibleIndexs(index) then
        if self._headIndex >= index then
            self:scrollToIndex(index, endCallback, 0,speed)
        else
            local cellModel = self._cellModels[index]
            if cellModel then
                self:scrollToIndex(index, endCallback, self._listViewheight - cellModel.size.height,speed)
            end
        end
    end
end


function ListViewVertical:scrollToOffset(targetOffsetY, endCallback, speed)
    self:stopScrollAction()
    if not speed then
        speed = SCROLL_SPEED
    end
    self._scrollEndCallBack = endCallback
    self._curScrollAction = display.createUpdateAction(function()
        local curOffsetY = self._innerContainer:getPositionY()
        local dy = targetOffsetY - curOffsetY
        local isFinish = false
        
        if dy < 0 then
            if dy >= -speed then
                isFinish = true  
            else
                dy = -speed
            end
            curOffsetY = curOffsetY + dy
        else
            if dy <= speed then
                isFinish = true  
            else
                dy = speed
            end
            curOffsetY = curOffsetY + dy
        end
        self._innerContainer:setPositionY(curOffsetY)
        self:_onScroll(curOffsetY)
        if isFinish then
            self:stopScrollAction(true)
        end
    end, 0.02)
    self._scrollView:runAction(self._curScrollAction)
end

-- projected:
function ListViewVertical:_reloadListView(params)
    params = params or {}
    local offsetY 
    if params.startIndex then
        local startIndex = params.startIndex or 1
        local startIndexOffset = params.startIndexOffset or 0
        offsetY = self:getOffsetByIndex(startIndex,startIndexOffset)
    elseif params.offsetY then
        offsetY = params.offsetY
        if offsetY > 0 then
            offsetY = 0
        end
        if offsetY < self._listViewheight - self._totalHeight then
            offsetY = self._listViewheight - self._totalHeight
        end
    else
        offsetY = self._innerContainer:getPositionY()
        if offsetY > 0 then
            offsetY = 0
        end
        if offsetY < self._listViewheight - self._totalHeight then
            offsetY = self._listViewheight - self._totalHeight
        end
    end
    self:setListViewOffsetY(offsetY)
end

function ListViewVertical:_init(params)
    local eventEnums = ccui.ScrollviewEventType
    self._eventListens = {
        [eventEnums.scrollToTop] = true,
        [eventEnums.scrollToBottom] = true,
        [eventEnums.scrolling] = true,
        [eventEnums.bounceTop] = true,
        [eventEnums.bounceBottom] = true,
        [eventEnums.bounceStart] = true,
        [eventEnums.bounceStop] = true,
    }

    -- 自动设置 间隔
    for k ,v in pairs(self._cellConfigs) do
        if v.perNum > 1 then
            local width = v.size.width
            v.gapX = (self._listViewWidth - self._paddingLeft - self._paddingRight - v.perNum * width)/(v.perNum - 1)
            v.gapX = math.floor(v.gapX)
        else
            v.gapX = 0
        end
    end
end

function ListViewVertical:_initCellModels(data, params)
    self._cellModels = {}
    -- local perNum = self._perNum
    -- local gapX = self._gapX
    local gapY = self._gapY
    local totalNum = #data
    local listViewWidth = self._listViewWidth
    self._realTotalNum = totalNum
    -- 单个cell 
    if self._isSingleTag then
        if params.minDataLen and  totalNum < params.minDataLen then
            totalNum = params.minDataLen
        end
        local perNum = self._cellConfigs["default"].perNum
        if params.forceAlign then
            totalNum = math.ceil(totalNum / perNum) * perNum
        end
    end
    local lastTag = nil
    local tempWidth = 0
    local maxHeight = 0
    local totalHeight = 0
    for i = 1, totalNum do
        local renderData = data[i]
        local tag = self:_getTagByData(renderData)
        local config = self._cellConfigs[tag]
        assert(config ~= nil, "can not find cell config  ".. tag or "nil")
        local size = config.size
        local gapX = config.gapX
        -- 换行
        if tempWidth ~= 0 and tempWidth + size.width > listViewWidth then
            totalHeight = totalHeight + maxHeight
            tempWidth = 0
            maxHeight = 0
        end

        local model = {
            pos = cc.p(tempWidth, -totalHeight), 
            tag = tag,
            size = size,
            index = i,
            cell = nil, 
            uiState = nil, 
            listview = self,        
        }
        if maxHeight < size.height + gapY then
            maxHeight = size.height + gapY
        end

        setmetatable(model, {__index = ListViewCellModelMetatable})
        self._cellModels[i] = model 
        tempWidth = tempWidth + size.width + gapX
    end
    self._totalNum = totalNum
    totalHeight = totalHeight + maxHeight - gapY + self._paddingBottom + self._paddingTop
    self._totalHeight = totalHeight
    if self._totalHeight < self._listViewheight then
        self._totalHeight = self._listViewheight
    end
    local cellHeight = self._totalHeight - self._paddingBottom - self._paddingTop
    self._cellsHeight = -cellHeight
    self._parentNode:setPositionY(cellHeight)
    self:_updateInnerContainerSize()
end

function ListViewVertical:_updateInnerContainerSize()
    self._ignoreScrollEvent = true
    self._scrollView:setInnerContainerSize(cc.size(self._listViewWidth, self._totalHeight))
    self._ignoreScrollEvent = false
end


function ListViewVertical:getTopOffsetYByIndex(index)
    if not index then
        return 0
    end
    local cellModel = self._cellModels[index]
    if cellModel then
        local offsetY = self._innerContainer:getPositionY()
        local top = self._cellsHeight - offsetY + self._listViewheight
        return top - cellModel.pos.y
    end
    return 0
end

function ListViewVertical:_reCreate(offsetY)
    self:_clean()
    if not offsetY then
        offsetY = self._innerContainer:getPositionY()
        if offsetY > 0 then
            offsetY = 0
        end
    
        if offsetY < self._listViewheight - self._totalHeight then
            offsetY = self._listViewheight - self._totalHeight
        end
    end
    local top = self._cellsHeight - offsetY + self._listViewheight
    local bottom = self._cellsHeight - offsetY - self._paddingBottom
    local gapY = self._gapY
    self._headIndex = 1
    self._tailIndex = 0
    
    local totalNum = self._totalNum
    local headIndex 
    for i = 1, totalNum do
        local cellModel = self._cellModels[i]
        if cellModel ~= nil and (cellModel.pos.y - cellModel.size.height - gapY) < top then
            headIndex = i
            break
        end
    end
    if headIndex then
        local tailIndex
        for i = headIndex, totalNum do
            local cellModel = self._cellModels[i]
            if cellModel ~= nil and cellModel.pos.y < bottom then
                tailIndex = i -1
                break
            end
            
            self:_newCellByModel(cellModel)
        end
        if not tailIndex then
            tailIndex = totalNum
        end
        self._headIndex = headIndex
        self._tailIndex = tailIndex
    end
end

function ListViewVertical:_onScroll(offsetY)
    if self._tailIndex == 0 then
        return
    end
    
    local totalNum = self._totalNum
    if offsetY > 0 then
        offsetY = 0
    end
    if offsetY < self._listViewheight - self._totalHeight then
        offsetY = self._listViewheight - self._totalHeight
    end

    
    local gapY = self._gapY

    local top = self._cellsHeight - offsetY + self._listViewheight
    local bottom = self._cellsHeight - offsetY - self._paddingBottom
    

    local startIndex = self._headIndex
    local endIndex = self._tailIndex
    local headCellModel = self._cellModels[startIndex]
    local endCellModel = self._cellModels[endIndex]
    if headCellModel and endCellModel and 
      ((headCellModel.pos.y - headCellModel.size.height - gapY) < top or endCellModel.pos.y > bottom) then
        if (endCellModel.pos.y - endCellModel.size.height - gapY) > bottom then
            local isBreak = false
            for i = startIndex, endIndex, 1 do
                local cellModel = self._cellModels[i]
                if cellModel ~= nil and (cellModel.pos.y - cellModel.size.height - gapY) > top then
                    self:_recycleCellByModel(cellModel)
                else
                    self._headIndex = i
                    isBreak = true
                    break
                end
            end
            if not isBreak then
                self._headIndex = endIndex
            end
            isBreak = false
            for i = endIndex + 1, totalNum, 1 do
                local cellModel = self._cellModels[i]
                if cellModel ~= nil and cellModel.pos.y > bottom then
                    self:_newCellByModel(cellModel)
                else
                    self._tailIndex = i - 1
                    isBreak = true
                    break
                end
            end
            if not isBreak then
                self._tailIndex = totalNum
            end
        elseif headCellModel.pos.y < top then
            local isBreak = false
            for i = endIndex, startIndex, -1 do
                local cellModel = self._cellModels[i]
                if cellModel ~= nil and cellModel.pos.y < bottom then
                    self:_recycleCellByModel(cellModel)
                else
                    self._tailIndex = i
                    isBreak = true
                    break
                end
            end
            if not isBreak then
                self._tailIndex = startIndex
            end

            --向上拖动
            isBreak = false
            for i = startIndex-1, 1, -1 do
                local cellModel = self._cellModels[i]
                if cellModel ~= nil and (cellModel.pos.y - cellModel.size.height - gapY) < top then
                    self:_newCellByModel(cellModel)
                else
                    self._headIndex = i + 1
                    isBreak = true
                    break
                end
            end
            if not isBreak then
                self._headIndex = 1
            end
        end
    else
        self:_reCreate()
    end
end

function ListViewVertical:_onEvent(scrollView, event)
    if self._allEventListen then
        self._allEventListen(scrollView, event)
    end
    -- 滚动事件  或者 滚动到底部或者 顶部
    if self._eventListens[event]  and self._ignoreScrollEvent == false then
        self:stopScrollAction()
        local offsetY = self._innerContainer:getPositionY()
        self:_onScroll(offsetY)
        
        if self._scrollEventListen then
            self._scrollEventListen(scrollView, event)
        end
    end
end


return ListViewVertical